摘要
Day 17 已經把契約 v1.0 / v1.1 的差異轉成可修正的證據。Day 18 把目前的資料品質閘門放進可重現的執行環境:新增 Dockerfile、Docker Compose 與 GitHub Actions,讓同一套 Maven test / package 流程可以在本機容器與 CI 上執行。
前面幾天已經可以回答:
這份 Bundle 在 JSON、FHIR R4、TW Core 與交換契約規則下能不能通過?
但如果這個答案只存在於我自己的電腦上,仍然不夠。
資料品質閘門要能支撐交換情境,至少還要回答另一個問題:
換一台乾淨環境,驗證結果還能不能重現?
這件事不只是部署問題。
它會影響測試證據的可信度。
如果本機可以跑、別人的環境不能跑,或 CI 沒有固定執行測試,那前面建立的規則、Expected / Actual 與版本比較,都很難被當成穩定成果。
所以 Day 18 的重點是:
把目前的驗證流程包成可建置、可啟動、可由 CI 檢查的最小交付單位。
今天新增或修改的範圍有:
Dockerfile
.dockerignore
docker-compose.yml
.github/workflows/ci.yml
README.md
20260819.md
目前六條規則、Quality Gate 與契約比較都已經完成最小版。
所以下一步補上 Docker 與 CI quality gate。
新增的 Dockerfile 採用兩階段:
FROM eclipse-temurin:17-jdk AS build
第一階段使用 JDK 建置專案。
專案目前 pom.xml 設定的是:
<java.version>17</java.version>
所以 Docker 和 CI 都先固定使用 Java 17。
Docker build 先複製 Maven wrapper 與 pom.xml:
COPY .mvn .mvn
COPY mvnw pom.xml ./
RUN chmod +x mvnw
RUN ./mvnw -q -DskipTests dependency:go-offline
這樣做的目的,是讓 Maven dependency 下載可以被 Docker layer cache 重用。
後續如果只改 Java 或 template,Docker 不一定要從頭下載全部 Maven dependency。
接著複製原始碼並打包:
COPY src src
RUN ./mvnw -q package
第二階段改用 JRE:
FROM eclipse-temurin:17-jre
只把 Spring Boot repackaged jar 複製到 runtime image:
COPY --from=build /workspace/target/twcore-data-quality-gate-0.0.1-SNAPSHOT.jar app.jar
最後開放 8080 並啟動:
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
容器建置時,Docker 會先把 build context 傳給 daemon。
如果沒有 .dockerignore,可能會把 .git、target、log 或其他本機檔案一起送進去。
這次先排除:
.git
.github
.mvn/wrapper/maven-wrapper.jar
target
*.log
.DS_Store
HELP.md
target 一定要排除。
Docker image 必須在容器裡自己跑 Maven build,不能偷偷依賴本機已經打好的 jar。
這樣才能驗證:
乾淨環境真的能從原始碼建出可執行應用程式。
新增的 docker-compose.yml 很小:
services:
twcore-data-quality-gate:
build: .
ports:
- "8080:8080"
目前 MVP 沒有資料庫,也沒有 queue、cache 或外部 service。
所以 Compose 只需要一個 service。
這樣使用者只要執行:
docker compose up --build
就能啟動整套 Spring Boot 應用程式。
啟動後開:
http://localhost:8080

Compose 啟動後,用:
docker compose ps
可以看到:
twcore-data-quality-gate-1 Up 0.0.0.0:8080->8080/tcp
再用 curl 檢查首頁:
curl -s -o /tmp/twcore-home.html -w '%{http_code}' http://localhost:8080
回傳:
200
並確認 HTML 裡包含:
TW Lab Contract Gate
這表示不是只有 container 處於 running 狀態,而是 Spring Boot app 真的有對外回應。
容器 log 也確認 runtime 使用 Java 17:
Starting TwcoreDataQualityGateApplication v0.0.1-SNAPSHOT using Java 17.0.19
Tomcat started on port 8080

Day 18 也新增:
.github/workflows/ci.yml
讓 CI 保護「59 個規則/Controller/情境比較測試」
workflow 觸發條件是:
on:
push:
pull_request:
也就是 push 和 pull request 都會跑。
使用 GitHub-hosted runner:
runs-on: ubuntu-latest
Java 設定固定為 Temurin 17:
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
cache: maven
然後執行兩個步驟:
- name: Run tests
run: ./mvnw test
- name: Build package
run: ./mvnw package

這裡刻意不在第一版 CI 加入 Docker build。
原因是今天的 minimum quality gate 先鎖定:
Maven test 必須通過。
Spring Boot package 必須能建出 jar。
之後如果要再提高 CI 門檻,可以另外補上 Docker image build。
本機 Maven 測試:
./mvnw test
結果:
Tests run: 59, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

本機 Maven package:
./mvnw package
結果:
BUILD SUCCESS
Docker Compose:
docker compose up --build
結果:
Image twcore-data-quality-gate-twcore-data-quality-gate Built
Container twcore-data-quality-gate-twcore-data-quality-gate-1 Started
HTTP 驗證:
HTTP 200 / homepage contains TW Lab Contract Gate
目前 Docker image 大小約:
493MB
如果執行:
docker info
看到類似:
failed to connect to the docker API
通常代表 Docker Desktop 還沒啟動完成。
這時要先開 Docker Desktop,等 daemon ready,再重跑 Docker 指令。
com.docker.vmnetd
如果 macOS 顯示 com.docker.vmnetd 被阻擋,不硬開未知來源。
這次處理方式是重新安裝 Docker Desktop,讓 Docker daemon 正常啟動後再驗證。
錯誤訊息:
bind: address already in use
排查:
lsof -nP -iTCP:8080 -sTCP:LISTEN
如果是舊的本機 Spring Boot app,停止它後再跑:
docker compose up -d
target 放進 Docker image如果 .dockerignore 沒有排除 target,Docker build 可能會意外吃到本機舊產物。
這會讓「乾淨環境可重建」的證據變弱。
所以 Day 18 明確把 target 排除,讓 image 必須自己從原始碼 build。
Dockerfile。.dockerignore。docker-compose.yml。./mvnw test 通過,測試數 59。./mvnw package 通過。docker compose up --build 成功建 image。docker compose up -d 成功啟動 container。http://localhost:8080 回傳 HTTP 200。Day 18 尚未處理:
目前的 MVP 進度:
validation-flow
├─ JSON parse 完成
├─ FHIR R4 parse 完成
├─ FHIR R4 validation 完成
├─ TW Core validation / safe NOT_EVALUATED 完成
├─ Exchange contract rules
│ ├─ LAB-REF-001 完成並接回畫面
│ ├─ LAB-REF-002 完成並接回畫面
│ ├─ LAB-REF-003 完成並接回畫面
│ ├─ LAB-CODE-001 完成並接回畫面
│ ├─ LAB-UNIT-001 完成並接回畫面
│ └─ LAB-UNIT-002 完成並接回畫面
├─ Quality Gate 完成最小版
├─ Contract comparison
│ ├─ ContractVersion 完成最小版
│ ├─ v1.0 / v1.1 rule selection 完成最小版
│ ├─ comparison service test 完成最小版
│ ├─ homepage comparison display 完成最小版
│ └─ upgrade blocker evidence display 完成最小版
├─ Scenario test pack
│ ├─ minimal expected / actual table 完成最小版
│ ├─ v1.0 / v1.1 representative cases 完成 4 例
│ └─ NOT_APPLICABLE scenario fixture 完成 1 例
└─ Reproducible delivery
├─ Dockerfile 完成最小版
├─ Docker Compose 完成最小版並驗證啟動
└─ GitHub Actions CI 完成最小版
下一步預計處理:
GitHub Actions 遠端綠燈確認
README 支援範圍與限制整理
Repository:twcore-data-quality-gate